home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 May / PCpro_2006_05.ISO / files / mobile / fma-2.0-stable-setup.exe / {app} / source / uMsgView.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2005-01-26  |  35.7 KB  |  1,147 lines

  1. unit uMsgView;
  2.  
  3. {
  4. *******************************************************************************
  5. * Descriptions: Implementation for SMS Listing
  6. * $Source: /cvsroot/fma/fma/uMsgView.pas,v $
  7. * $Locker:  $
  8. *
  9. * Todo:
  10. *
  11. * Change Log:
  12. * $Log: uMsgView.pas,v $
  13. * Revision 1.38.6.1  2005/01/25 16:03:09  z_stoichev
  14. * Merged with 2.1 Beta 1 bugfixes
  15. *
  16. * Revision 1.38  2004/07/14 09:35:40  z_stoichev
  17. * - Fixed Delete text messages (stay on phone issue).
  18. * - Fixed Clear message preview/image when deleted.
  19. *
  20. * Revision 1.37  2004/07/07 11:21:07  z_stoichev
  21. * Fixed delete multiple messages.
  22. *
  23. * Revision 1.36  2004/07/06 14:06:53  z_stoichev
  24. * - Added Personalization default contact image.
  25. *
  26. * Revision 1.35  2004/06/29 12:37:07  z_stoichev
  27. * New message window renamed
  28. *
  29. * Revision 1.34  2004/06/28 23:01:15  z_stoichev
  30. * Delete message bugfixes.
  31. *
  32. * Revision 1.33  2004/06/25 08:15:42  z_stoichev
  33. * No quotes in imported message PDUs.
  34. *
  35. * Revision 1.32  2004/06/24 09:28:03  z_stoichev
  36. * Fixed unknown sender name lookup.
  37. *
  38. * Revision 1.31  2004/06/24 09:08:29  z_stoichev
  39. * - Added Chat to Contact
  40. *
  41. * Revision 1.30  2004/06/23 13:53:03  z_stoichev
  42. * Added Chat support
  43. *
  44. * Revision 1.29  2004/06/22 14:38:55  z_stoichev
  45. * - Fixed Export file type filter misusage/order.
  46. * - Fixed Export messages UTF-8 and html encoding.
  47. *
  48. * Revision 1.28  2004/06/08 19:20:50  lordlarry
  49. * Memory Leak fixed
  50. *
  51. * Revision 1.27  2004/05/19 18:34:16  z_stoichev
  52. * Build 0.1.0.35c
  53. *
  54. * Revision 1.26  2004/03/14 18:49:29  z_stoichev
  55. * Mark message as read/unread access state.
  56. * Changed colors.
  57. *
  58. * Revision 1.25  2004/03/09 15:06:29  z_stoichev
  59. * Added Delete Text messages progress dialog.
  60. *
  61. * Revision 1.24  2004/01/30 16:41:12  z_stoichev
  62. * Fixed Send contacts from SIM to Phone AV error.
  63. * Added Progress dialog on Uploading contacts.
  64. * Added Fma windows update on SMS Delete/Upload.
  65. *
  66. * Revision 1.23  2004/01/28 17:39:53  z_stoichev
  67. * Popup menu rearranged.
  68. *
  69. * Revision 1.22  2004/01/23 08:33:18  z_stoichev
  70. * Fixed numbers only shown on startup.
  71. * Fixed upload to phone routines.
  72. *
  73. * Revision 1.21  2004/01/15 14:15:12  z_stoichev
  74. * GUI Changes.
  75. *
  76. * Revision 1.20  2004/01/13 12:28:06  z_stoichev
  77. * Added New menu icons.
  78. *
  79. * Revision 1.19  2004/01/12 15:36:33  z_stoichev
  80. * Fixed Export to CSV.
  81. * Added Import from CSV.
  82. *
  83. * Revision 1.18  2003/12/18 12:44:16  z_stoichev
  84. * Drafts folder doesn't have auto select as read feature.
  85. * Send Message icon set.
  86. *
  87. * Revision 1.17  2003/12/17 16:57:25  z_stoichev
  88. * Added Send Message from Drafts.
  89. * Fixed Mark as Unread enabled state.
  90. *
  91. * Revision 1.16  2003/12/16 17:40:44  z_stoichev
  92. * Ignore incorrect data (corruptedt pdu).
  93. *
  94. * Revision 1.15  2003/12/12 16:54:24  z_stoichev
  95. * Added view customization support.
  96. *
  97. * Revision 1.14  2003/12/09 12:05:25  z_stoichev
  98. * Build 0.10.28c + 29a changes without new WaitComplete unit.
  99. *
  100. * Revision 1.13  2003/12/04 16:33:33  z_stoichev
  101. * Fixed Message Perview disappear when too low.
  102. * Added Inbox new messages number into Explorer.
  103. * Added timestamp to outgoing/archive messages.
  104. * Added Action buttons into message preview.
  105. *
  106. * Revision 1.12  2003/12/01 12:02:26  z_stoichev
  107. * Hit Enter shows properties window.
  108. *
  109. * Revision 1.11  2003/11/28 09:38:07  z_stoichev
  110. * Merged with branch-release-1-1 (Fma 0.10.28c)
  111. *
  112. * Revision 1.10.2.7  2003/11/27 16:05:53  z_stoichev
  113. * Fixed delete archive connection request.
  114. *
  115. * Revision 1.10.2.6  2003/11/14 15:41:02  z_stoichev
  116. * Updates for patch 27d.
  117. *
  118. * Revision 1.10.2.5  2003/11/13 16:38:36  z_stoichev
  119. * Changed images.
  120. * Added personalization support (sender image).
  121. *
  122. * Revision 1.10.2.4  2003/11/12 16:31:02  z_stoichev
  123. * Add voice call to sender.
  124. *
  125. * Revision 1.10.2.3  2003/11/06 16:28:47  z_stoichev
  126. * Action update state changed.
  127. * Bugfixes.
  128. *
  129. * Revision 1.10.2.2  2003/10/28 12:57:06  z_stoichev
  130. * Fixed upload to phone popup menu.
  131. *
  132. * Revision 1.10.2.1  2003/10/27 07:22:54  z_stoichev
  133. * Build 0.1.0 RC1 Initial Checkin.
  134. *
  135. * Revision 1.10  2003/10/23 12:29:45  z_stoichev
  136. * Double-click opens SMS details view.
  137. * Popup menu recreated.
  138. * Font changed.
  139. *
  140. * Revision 1.9  2003/10/15 11:10:23  z_stoichev
  141. * Fixed bug 823902: Move SMS from Archive to Phone/SIM.
  142. *
  143. * Revision 1.8  2003/10/10 13:27:44  z_stoichev
  144. * Default message "No items to display..."
  145. *
  146. * Revision 1.7  2003/07/02 12:38:57  crino77
  147. * little modify on sort
  148. * export xml csv
  149. * full unicode support
  150. * added header in csv file
  151. *
  152. * Revision 1.6  2003/02/25 07:05:11  crino77
  153. * Added popup menu in MemoMsgBody
  154. * Changed font size in MemoMsgBody
  155. *
  156. * Revision 1.5  2003/02/14 11:44:18  crino77
  157. * Changed MEMO > RichEdit
  158. * Added support to decode EMS
  159. * Added ExportList for export in html
  160. * Added 'MemoMsgBody.clear' to remove the last msg viewed
  161. *
  162. * Revision 1.4  2003/01/30 04:15:57  warren00
  163. * Updated with header comments
  164. *
  165. *
  166. *******************************************************************************
  167. }
  168.  
  169. interface
  170.  
  171. uses 
  172.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  173.   Menus, ImgList, VirtualTrees, ExtCtrls, StdCtrls, TntStdCtrls, ComCtrls,
  174.   TntComCtrls, GR32_Image, Buttons, Placemnt, LMDControl, LMDBaseControl,
  175.   LMDBaseGraphicControl, LMDGraphicControl, LMDFill;
  176.  
  177. type
  178.   TfrmMsgView = class(TFrame)
  179.     Splitter2: TSplitter;
  180.     ListMsg: TVirtualStringTree;
  181.     ImageList: TImageList;
  182.     pmListMsg: TPopupMenu;
  183.     Reply1: TMenuItem;
  184.     Forward1: TMenuItem;
  185.     N6: TMenuItem;
  186.     Archive1: TMenuItem;
  187.     MovetoArchive1: TMenuItem;
  188.     Delete1: TMenuItem;
  189.     N15: TMenuItem;
  190.     Detail1: TMenuItem;
  191.     N1: TMenuItem;
  192.     ExportselectedSMS1: TMenuItem;
  193.     pmRich: TPopupMenu;
  194.     Copy1: TMenuItem;
  195.     SendTO: TMenuItem;
  196.     SIM1: TMenuItem;
  197.     Phone1: TMenuItem;
  198.     StoreAsSent1: TMenuItem;
  199.     StoreAsUnsent1: TMenuItem;
  200.     StoreAsSent2: TMenuItem;
  201.     StoreAsUnsent2: TMenuItem;
  202.     N2: TMenuItem;
  203.     NoItemsPanel: TPanel;
  204.     Newmessage1: TMenuItem;
  205.     contactcall1: TMenuItem;
  206.     N5: TMenuItem;
  207.     PreviewPanel: TPanel;
  208.     MemoMsgBody: TTntRichEdit;
  209.     Panel3: TPanel;
  210.     Panel2: TPanel;
  211.     SelImage: TImage32;
  212.     Panel4: TPanel;
  213.     SpeedButton1: TSpeedButton;
  214.     addcontact1: TMenuItem;
  215.     SpeedButton2: TSpeedButton;
  216.     SpeedButton3: TSpeedButton;
  217.     SpeedButton4: TSpeedButton;
  218.     SpeedButton5: TSpeedButton;
  219.     Bevel1: TBevel;
  220.     Timer1: TTimer;
  221.     MarkasRead1: TMenuItem;
  222.     MarkasUnread1: TMenuItem;
  223.     FormStorage1: TFormStorage;
  224.     SendMessage1: TMenuItem;
  225.     ImportTextMessages1: TMenuItem;
  226.     OpenDialog1: TOpenDialog;
  227.     Label1: TLabel;
  228.     LMDFill2: TLMDFill;
  229.     StoredReadItems1: TMenuItem;
  230.     StoredUnreadItems1: TMenuItem;
  231.     N4: TMenuItem;
  232.     ChatContact1: TMenuItem;
  233.     N7: TMenuItem;
  234.     procedure ListMsgBeforeCellPaint(Sender: TBaseVirtualTree;
  235.       TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  236.       CellRect: TRect);
  237.     procedure ListMsgCompareNodes(Sender: TBaseVirtualTree; Node1,
  238.       Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
  239.     procedure ListMsgFocusChanged(Sender: TBaseVirtualTree;
  240.       Node: PVirtualNode; Column: TColumnIndex);
  241.     procedure ListMsgGetImageIndex(Sender: TBaseVirtualTree;
  242.       Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  243.       var Ghosted: Boolean; var ImageIndex: Integer);
  244.     procedure ListMsgGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  245.       Column: TColumnIndex; TextType: TVSTTextType;
  246.       var CellText: WideString);
  247.     procedure ListMsgHeaderClick(Sender: TVTHeader; Column: TColumnIndex;
  248.       Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  249.     procedure Detail1Click(Sender: TObject);
  250.     procedure Copy1Click(Sender: TObject);
  251.     procedure StoreAsSent1Click(Sender: TObject);
  252.     procedure StoreAsUnsent1Click(Sender: TObject);
  253.     procedure StoreAsSent2Click(Sender: TObject);
  254.     procedure StoreAsUnsent2Click(Sender: TObject);
  255.     procedure ListMsgAfterPaint(Sender: TBaseVirtualTree;
  256.       TargetCanvas: TCanvas);
  257.     procedure pmListMsgPopup(Sender: TObject);
  258.     procedure btnDELClick(Sender: TObject);
  259.     procedure SpeedButton1Click(Sender: TObject);
  260.     procedure ListMsgKeyDown(Sender: TObject; var Key: Word;
  261.       Shift: TShiftState);
  262.     procedure Splitter2Moved(Sender: TObject);
  263.     procedure ListMsgChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
  264.     procedure Timer1Timer(Sender: TObject);
  265.     procedure MarkasReadUnreadClick(Sender: TObject);
  266.     procedure ListMsgDblClick(Sender: TObject);
  267.     procedure SendMessage1Click(Sender: TObject);
  268.     procedure ImportTextMessages1Click(Sender: TObject);
  269.     procedure StoredReadItems1Click(Sender: TObject);
  270.     procedure StoredUnreadItems1Click(Sender: TObject);
  271.   private
  272.     FCustomImage: Boolean;
  273.     function FlattenText(str: WideString): WideString;
  274.     procedure ShowDetail;
  275.     procedure WriteSMS(Mem: String; State: Integer = -1);
  276.     procedure Set_CustomImage(const Value: Boolean);
  277.   public
  278.     procedure RenderListView(const sl: TStrings);
  279.     procedure ExportList(FileType:Integer; Filename: WideString);
  280.     property IsCustomImage: Boolean read FCustomImage write Set_CustomImage;
  281.   end;
  282.  
  283.   TLookUpNumberThread = class(TThread)
  284.   private
  285.     FListMsg: TVirtualStringTree;
  286.     node: PVirtualNode;
  287.     str: String;
  288.   protected
  289.     procedure Execute; override;
  290.     procedure UpdateCaption;
  291.   public
  292.     constructor Create(ListMsg: TVirtualStringTree);
  293.   end;
  294.  
  295.   TListData = Record
  296.     imageindex: Integer;
  297.     stateindex: Integer;
  298.     from, msg: WideString;
  299.     pdu: String;
  300.     date: TDateTime;
  301.     newmsg: Boolean;
  302.   end;
  303.   PListData = ^TListData;
  304.  
  305. implementation
  306.  
  307. {$R *.dfm}
  308.  
  309. uses uMissedCalls, uSyncPhonebook, DateUtils, Unicode, Unit1, gsm_sms, uGlobal, uSMSDetail,
  310.   uComposeSMS, uConnProgress, WebUtil;
  311.  
  312. function TfrmMsgView.FlattenText(str: WideString): WideString;
  313. var
  314.   sl: TWideStrings;
  315.   i: Integer;
  316. begin
  317.   sl := TWideStringList.Create;
  318.   sl.Text := str;
  319.  
  320.   for i := 0 to sl.Count - 1 do begin
  321.     Result := Result + WideChar(#32) + sl.Strings[i];
  322.   end;
  323.   sl.Destroy;
  324.  
  325.   Result := WideTrim(Result);
  326. end;
  327.  
  328. procedure TfrmMsgView.ListMsgBeforeCellPaint(Sender: TBaseVirtualTree;
  329.   TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex;
  330.   CellRect: TRect);
  331. var
  332.   item: PListData;
  333. begin
  334.   item := Sender.GetNodeData(Node);
  335.  
  336.   if Node <> Sender.FocusedNode then begin
  337.     if item.newmsg then begin
  338.       TargetCanvas.Brush.Color := $00D0F8D0; // all columns (new msg)
  339.       TargetCanvas.FillRect(CellRect);
  340.     end
  341.     else
  342.     if Column = 0 then begin
  343.       if item.imageindex = 17 then
  344.         TargetCanvas.Brush.Color := $00E0E0FF  // from column (out)
  345.       else
  346.         TargetCanvas.Brush.Color := $00FFE0E0; // from column (in)
  347.       TargetCanvas.FillRect(CellRect);
  348.     end;
  349.   end;
  350. end;
  351.  
  352. procedure TfrmMsgView.ListMsgCompareNodes(Sender: TBaseVirtualTree; Node1,
  353.   Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
  354. var
  355.   item1, item2: PListData;
  356. begin
  357.   item1 := Sender.GetNodeData(Node1);
  358.   item2 := Sender.GetNodeData(Node2);
  359.  
  360.   if Column = 0 then Result := CompareStr(item1.from, item2.from)
  361.   else if Column = 1 then Result := WideCompareStr(item1.msg, item2.msg)
  362.   else if Column = 2 then Result := CompareDateTime(item1.date, item2.date);
  363. end;
  364.  
  365. procedure TfrmMsgView.ListMsgFocusChanged(Sender: TBaseVirtualTree;
  366.   Node: PVirtualNode; Column: TColumnIndex);
  367. const
  368.   LastImage: string = '';  
  369. var
  370.   item: PListData;
  371.   sms: TSMS;
  372.   UDHI: String;
  373.   pos, octet, {posTemp,} udhil: Integer;
  374.   Description: String;
  375.   contact: PContactData;
  376.   s: WideString;
  377. begin
  378.   IsCustomImage := False;
  379.   if Node = nil then Exit;
  380.  
  381.   sms := TSMS.Create;//ADD
  382.   try
  383.     //posTemp := 0;
  384.     item := Sender.GetNodeData(Node);
  385.     MemoMsgBody.Text := item.msg;// + inttostr(length(item.msg));
  386.     MemoMsgBody.DefAttributes.Color := clWindowText;
  387.     MemoMsgBody.DefAttributes.Size  := 10;
  388.     sms.PDU := item.pdu; //ADD  {
  389.     { try to load contact personalized image and maintain a cache }
  390.     s := Form1.LookupContact(sms.Number);
  391.     if Form1.frmSyncPhonebook.FindContact(s,contact) then begin
  392.       s := GetContactPictureFile(contact);
  393.       if s <> '' then
  394.         try
  395.           { cache loaded image name }
  396.           if not IsCustomImage or (s <> LastImage) then begin
  397.             SelImage.Bitmap.LoadFromFile(s);
  398.             IsCustomImage := True;
  399.             LastImage := s;
  400.           end;
  401.         except
  402.           IsCustomImage := False;
  403.         end
  404.       else
  405.         IsCustomImage := False;
  406.     end
  407.     else
  408.         IsCustomImage := False;
  409.     { show message info }
  410.     item.msg := sms.Text;
  411.     if sms.IsUDH then begin
  412.        UDHI := sms.UDHI;
  413.        udhil := StrToInt('$' + copy(UDHI, 1, 2));
  414.        //ANALIZE UDHI
  415.        UDHI := Copy(UDHI, 3, length(UDHI));
  416.        //pos := 1;
  417.        repeat
  418.           //Get the octet for type
  419.           octet := StrToInt('$' + Copy(UDHI, 1, 2));
  420.           UDHI := Copy(UDHI, 3, length(UDHI));
  421.           case octet of
  422.              0:begin //SMS CONCATENATION
  423.                pos := StrToInt('$' + Copy(UDHI, 1, 2)) + 1;
  424.                Description := Description + '[SMS Concatenation';
  425.                Description := Description + ' - SMS REF:' + IntToStr(StrToInt('$' + Copy(UDHI, 3, 2)));
  426.                Description := Description + ' - SMS TOT:' + IntToStr(StrToInt('$' + Copy(UDHI, 5, 2)));
  427.                Description := Description + ' - SMS N:' + IntToStr(StrToInt('$' + Copy(UDHI, 7, 2))) + ']';
  428.                UDHI := Copy(UDHI, pos*2+1, length(UDHI));
  429.                end;
  430.             {10:begin //TEXT FORMATTING
  431.                pos := StrToInt('$' + Copy(UDHI, 1, 2)) + 1 ;
  432.                MemoMsgBody.SelStart := StrToInt('$' + Copy(UDHI, 3, 2));
  433.                MemoMsgBody.SelLength := Length(MemoMsgBody.text) - MemoMsgBody.SelStart;
  434.                UDHI := Copy(UDHI, pos*2+1, length(UDHI));
  435.                Description := Description + '[EMS TEXT FORMATTING]';
  436.                end;
  437.             11:begin //SOUND
  438.                pos := StrToInt('$' + Copy(UDHI, 1, 2)) + 1;
  439.                MemoMsgBody.SelStart := posTemp + StrToInt('$' + Copy(UDHI, 3, 2));
  440.                MemoMsgBody.SelText := '[SOUND TYPE: ' + Copy(UDHI, 5, 2) + ']';
  441.                posTemp := posTemp + 16 ;
  442.                UDHI := Copy(UDHI, pos*2+1, length(UDHI));
  443.                end;}
  444.              else begin
  445.                pos := udhil + 1;
  446.                UDHI := Copy(UDHI, pos*2+1, length(UDHI));
  447.                Description := '[EMS]';
  448.              end
  449.           end;
  450.        until UDHI = '';
  451.        MemoMsgBody.SelStart := length(MemoMsgBody.text);
  452.        MemoMsgBody.selAttributes.Color := clGray;
  453.        MemoMsgBody.selAttributes.Size := 8;
  454.        MemoMsgBody.Lines.add(Description);
  455.     end;   //ADD   }
  456.   finally
  457.     sms.Destroy;   //ADD
  458.   end;  
  459. end;
  460.  
  461. procedure TfrmMsgView.ListMsgGetImageIndex(Sender: TBaseVirtualTree;
  462.   Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  463.   var Ghosted: Boolean; var ImageIndex: Integer);
  464. var
  465.   item: PListData;
  466. begin
  467.   if Column = 0 then begin
  468.     if (Kind = ikNormal) or (Kind = ikSelected) then begin
  469.       item := Sender.GetNodeData(Node);
  470.       ImageIndex := item.imageindex;
  471.     end
  472.     else ImageIndex := -1;
  473.   end;
  474. end;
  475.  
  476. procedure TfrmMsgView.ListMsgGetText(Sender: TBaseVirtualTree;
  477.   Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  478.   var CellText: WideString);
  479. var
  480.   item: PListData;
  481. begin
  482.   item := Sender.GetNodeData(Node);
  483.  
  484.   if Column = 0 then CellText := item.from
  485.   else if Column = 1 then CellText := FlattenText(item.msg)
  486.   else if Column = 2 then begin
  487.     if item.date > 0 then begin
  488.       if isToday(item.date) then CellText := TimeToStr(item.date)
  489.       else CellText := DateTimeToStr(item.date)
  490.     end
  491.     else CellText := '';
  492.   end;
  493. end;
  494.  
  495. procedure TfrmMsgView.ListMsgHeaderClick(Sender: TVTHeader;
  496.   Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
  497.   Y: Integer);
  498. begin
  499.   if Column = Sender.SortColumn then begin
  500.     if Sender.SortDirection = sdDescending then
  501.       Sender.SortDirection := sdAscending
  502.     else
  503.       Sender.SortDirection := sdDescending;
  504.   end
  505.   else
  506.     Sender.SortColumn := Column;
  507. end;
  508.  
  509. procedure TfrmMsgView.RenderListView(const sl: TStrings);
  510. var
  511.   i: Integer;
  512.   sms: TSMS;
  513.   item: PListData;
  514.   Node: PVirtualNode;
  515.   dt: string;
  516. begin
  517.   MemoMsgBody.Clear;//ADD
  518.   FCustomImage := True; //Force image reload
  519.   IsCustomImage := False;
  520.   ListMsg.BeginUpdate;
  521.   try
  522.     ListMsg.Clear;
  523.     ListMsg.NodeDataSize := sizeof(TListData);
  524.  
  525.     for i := 0 to sl.Count - 1 do begin
  526.       Node := ListMsg.AddChild(nil);
  527.       try
  528.         item := ListMsg.GetNodeData(Node);
  529.         item.pdu := GetToken(sl[i], 5);
  530.  
  531.         sms := Tsms.Create;
  532.         try
  533.           sms.PDU := item.pdu;
  534.  
  535.           item.from := sms.Number;
  536.           item.date := sms.TimeStamp;
  537.           item.msg := sms.Text;
  538.  
  539.           item.stateindex := StrToInt(GetToken(sl[i], 1)) and $FFFF; // index
  540.  
  541.           if StrToInt(GetToken(sl[i], 0)) = 1 then begin // ME
  542.             item.ImageIndex := 14;
  543.             item.StateIndex := item.StateIndex or $600000;
  544.           end
  545.           else if StrToInt(GetToken(sl[i], 0)) = 2 then begin // SM
  546.             item.ImageIndex := 15;
  547.             item.StateIndex := item.StateIndex or $640000;
  548.           end
  549.           else begin // PC
  550.             if sms.IsOutgoing then item.ImageIndex := 17
  551.             else item.ImageIndex := 16;
  552.             item.StateIndex := item.StateIndex or $680000;
  553.           end;
  554.  
  555.           // Direction Bit
  556.           if sms.IsOutgoing then item.StateIndex := item.StateIndex or $020000
  557.           else item.StateIndex := item.StateIndex or $010000;
  558.  
  559.           // New fields in 0.10.29a build
  560.           try
  561.             dt := GetToken(sl[i], 6);
  562.             if dt <> '' then item.date := StrToDateTime(dt);
  563.             item.newmsg := StrToInt(GetToken(sl[i], 7)) <> 0;
  564.           except
  565.           end;
  566.         finally
  567.           sms.Free;
  568.         end;
  569.       except
  570.         ListMsg.DeleteNode(Node);
  571.         Form1.Status('Error: Incorrect data ignored (Index: '+IntToStr(i)+', PDU: '+GetToken(sl[i], 5)+')');
  572.       end;
  573.     end;
  574.   finally
  575.     //ListMsg.Sort(nil, ListMsg.Header.SortColumn, ListMsg.Header.SortDirection);
  576.     ListMsg.EndUpdate;
  577.     if not Application.Terminated then
  578.       TLookUpNumberThread.Create(ListMsg);
  579.   end;
  580. end;
  581.  
  582. {TLookUpNumberThread}
  583.  
  584. constructor TLookUpNumberThread.Create(ListMsg: TVirtualStringTree);
  585. begin
  586.   FListMsg := ListMsg;
  587.   FreeOnTerminate := True;
  588.   inherited Create(False);
  589. end;
  590.  
  591. procedure TLookUpNumberThread.Execute;
  592. var
  593.   sl: TStrings;
  594.   item: PListData;
  595. begin
  596.   { TODO -oLordLarry : This whole function is very thread UNSAFE }
  597.   sl := TStringList.Create;
  598.   try
  599.     with FListMsg do begin
  600.       node := GetFirst;
  601.       if node <> nil then repeat
  602.         try
  603.           item := GetNodeData(node);
  604.           item.from := Form1.ExtractNumber(item.from);
  605.           str := sl.Values[item.from];
  606.           if sl.IndexOfName(item.from) = -1 then begin
  607.             str := Form1.LookupContact(item.from,sUnknownContact);
  608.             sl.Add(item.from + '=' + str);
  609.           end;
  610.           if str <> '' then begin
  611.             item.from := str + ' [' + item.from + ']';
  612.           end;
  613.         except
  614.         end;
  615.         node := GetNext(node);
  616.       until Application.Terminated or (node = nil);
  617.     end;
  618.     if not Application.Terminated then
  619.       Synchronize(UpdateCaption);
  620.   finally
  621.     sl.Destroy;
  622.   end;  
  623. end;
  624.  
  625. procedure TLookUpNumberThread.UpdateCaption;
  626. begin
  627.   if FListMsg <> nil then FListMsg.Repaint;
  628. end;
  629.  
  630. procedure TfrmMsgView.ShowDetail;
  631. var
  632.   node: PVirtualNode;
  633.   item: PListData;
  634. begin
  635.   node := ListMsg.FocusedNode;
  636.   if node <> nil then begin
  637.     item := ListMsg.GetNodeData(node);
  638.     if item <> nil then begin
  639.       if frmDetail = nil then
  640.         frmDetail := TfrmDetail.Create(Self);
  641.       frmDetail.PDU := item.pdu;
  642.       frmDetail.Show;
  643.     end;
  644.   end;  
  645. end;
  646.  
  647. procedure TfrmMsgView.Detail1Click(Sender: TObject);
  648. begin
  649.   Timer1.Enabled := False;
  650.   Timer1.Interval := 100;
  651.   Timer1.Enabled := (ListMsg.FocusedNode <> nil) and (Form1.Explorer.Selected <> Form1.FNodeMsgDrafts);
  652.   ShowDetail;
  653. end;
  654.  
  655. procedure TfrmMsgView.ExportList(FileType:Integer; Filename: WideString);
  656. var
  657.   node: PVirtualNode;
  658.   item: PListData;
  659.   sl : TStringList;
  660.   t,s,str : WideString;
  661. begin
  662.   case FileType of
  663.     1:begin//CSV
  664.         sl := TStringList.Create;
  665.         str := '"Subject","Body","From: (Name)","From: (Address)","From: (Type)","To: (Name)","To: (Address)","To: (Type)",'+
  666.           '"Fma Date","Fma State","Fma PDU","Fma IsNew"';
  667.         sl.add(str);
  668.         with ListMsg do begin
  669.           node := GetFirst;
  670.           repeat
  671.              try
  672.                 if Selected[node] then begin
  673.                    item := GetNodeData(node);
  674.                    str := '"SMS","' + StringReplace(item.msg, '"', '""', [rfReplaceAll]) + '",';
  675.                    s := Form1.ExtractNumber(item.from);
  676.                    t := Form1.LookupContact(s,sUnknownContact) + ' [' + s + ']';
  677.                    if item.StateIndex and $020000 <> 0 then begin // outgoing message
  678.                      str := str + '"(Outgoing)","' + t + '","PHONE",';
  679.                      str := str + '"' + Form1.LookupContact(s) + '","' + s + '","PHONE",';
  680.                    end
  681.                    else begin
  682.                      str := str + '"' + Form1.LookupContact(s) + '","' + s + '","PHONE",';
  683.                      str := str + '"(Incoming)","' + t + '","PHONE",';
  684.                    end;
  685.                    if item.date > 0 then str := str + '"' + DateTimeToStr(item.date) + '",'
  686.                      else str := str + '"",';
  687.                    str := str + '"' + IntToStr(item.stateindex) + '","' + item.pdu + '","' + IntToStr(byte(item.newmsg)) + '"';
  688.                    sl.add(str);
  689.                 end;
  690.              except
  691.              end;
  692.              node := GetNext(node);
  693.           until node = nil;
  694.         end;
  695.         sl.SaveToFile(FileName);
  696.         sl.Destroy;
  697.        end;
  698.     2:begin//XML
  699.         sl := TStringList.Create;
  700.         sl.Add('<?xml version="1.0" encoding="utf-8" ?>');
  701.         sl.Add('<fma_messages>');
  702.         with ListMsg do begin
  703.           node := GetFirst;
  704.           repeat
  705.              try
  706.                 if Selected[node] then begin
  707.                    item := GetNodeData(node);
  708.                    str := '<sms>';
  709.                    str := str + '<from>' + HTMLEncode(UTF8Encode(item.from),False) + '</from>';
  710.                    str := str + '<msg>' + HTMLEncode(UTF8Encode(item.msg),False) + '</msg>';
  711.                    if item.date > 0 then
  712.                       str := str + '<date>' + HTMLEncode(UTF8Encode(DateTimeToStr(item.date)),False) + '</date>'
  713.                    else str := str + '<date/>';
  714.                    str := str + '</sms>';
  715.                    sl.add(str);
  716.                 end;
  717.              except
  718.              end;
  719.              node := GetNext(node);
  720.           until node = nil;
  721.         end;
  722.         sl.Add('</fma_messages>');
  723.         sl.SaveToFile(FileName);
  724.         sl.Destroy;
  725.       end;
  726.     3:begin//HTML
  727.         sl := TStringList.Create;
  728.         sl.Add('<html><head><meta content="text/html;charset=utf-8" http-equiv="content-type">');
  729.         sl.Add('<title>FMA Messages</title></head><body>');
  730.         sl.Add('<TABLE BORDER="1">');
  731.         sl.Add('<TR><TD>FROM</TD><TD>BODY</TD><TD>DATE</TD></TR>');
  732.         with ListMsg do begin
  733.           node := GetFirst;
  734.           repeat
  735.              try
  736.                 if Selected[node] then begin
  737.                    item := GetNodeData(node);
  738.                    str := '<TR>';                                
  739.                    str := str + '<TD>' + HTMLEncode(UTF8Encode(item.from),False) + '</TD>';
  740.                    str := str + '<TD>' + HTMLEncode(UTF8Encode(item.msg),False) + '</TD>';
  741.                    if item.date > 0 then
  742.                       str := str + '<TD>' + HTMLEncode(UTF8Encode(DateTimeToStr(item.date)),False) + '</TD>'
  743.                    else str := str + '<TD></TD>';
  744.                    str := str + '</TR>';
  745.                    sl.add(str);
  746.                 end;
  747.              except
  748.              end;
  749.              node := GetNext(node);
  750.           until node = nil;
  751.         end;
  752.         sl.Add('</TABLE>');
  753.         sl.Add('</body></html>');
  754.         sl.SaveToFile(FileName);
  755.         sl.Destroy;
  756.       end;
  757.   end;
  758. end;
  759.  
  760. procedure TfrmMsgView.Copy1Click(Sender: TObject);
  761. begin
  762.     memomsgbody.CopyToClipboard;
  763. end;
  764.  
  765. procedure TfrmMsgView.StoreAsUnsent1Click(Sender: TObject);
  766. begin
  767.    WriteSMS('SM'); // default 0
  768. end;
  769.  
  770. procedure TfrmMsgView.StoreAsSent1Click(Sender: TObject);
  771. begin
  772.    WriteSMS('SM', 1);
  773. end;
  774.  
  775. procedure TfrmMsgView.StoreAsUnsent2Click(Sender: TObject);
  776. begin
  777.    WriteSMS('ME'); // default 0
  778. end;
  779.  
  780. procedure TfrmMsgView.StoreAsSent2Click(Sender: TObject);
  781. begin
  782.    WriteSMS('ME', 1);
  783. end;
  784.  
  785. procedure TfrmMsgView.StoredUnreadItems1Click(Sender: TObject);
  786. begin
  787.   WriteSMS('ME', 2);
  788. end;
  789.  
  790. procedure TfrmMsgView.StoredReadItems1Click(Sender: TObject);
  791. begin
  792.   WriteSMS('ME', 3);
  793. end;
  794.  
  795. procedure TfrmMsgView.WriteSMS(Mem: String; State: Integer);
  796. var
  797.   node,root,tmp: PVirtualNode;
  798.   item: PListData;
  799.   sms: Tsms;
  800.   procedure DelNode;
  801.   var
  802.     i, index, location: Integer;
  803.   begin
  804.     index := item.StateIndex and $FFFF;
  805.     location := ((item.StateIndex and $0C0000) shr 18) + 1;
  806.  
  807.     for i := 0 to TStrings(Form1.Explorer.Selected.Data).Count - 1 do begin
  808.       if GetToken(TStrings(Form1.Explorer.Selected.Data).Strings[i], 1) = IntToStr(index) then begin
  809.         if GetToken(TStrings(Form1.Explorer.Selected.Data).Strings[i], 0) = IntToStr(location) then begin
  810.           TStrings(Form1.Explorer.Selected.Data).Delete(i);
  811.           Form1.UpdateNewMessagesCounter(Form1.Explorer.Selected);
  812.           break;
  813.         end;
  814.       end;
  815.     end;
  816.     ListMsg.DeleteNode(node);
  817.   end;
  818. begin
  819.   { State = -1 (default) means write as "received unread" }
  820.   sms := Tsms.Create;
  821.   ListMsg.BeginUpdate;
  822.   try
  823.     with ListMsg do begin
  824.       node := GetFirst;
  825.       root := node;
  826.       repeat
  827.         try
  828.           if Selected[node] then begin
  829.             item := GetNodeData(node);
  830.             sms.PDU := item.pdu;
  831.             if Form1.WriteSMS(Mem, item.pdu, State) then begin
  832.               if node = root then begin
  833.                 DelNode;
  834.                 node := GetFirst;
  835.                 root := node;
  836.                 if root = nil then break;
  837.               end
  838.               else begin
  839.                 tmp := GetPrevious(node);
  840.                 DelNode;
  841.                 node := tmp;
  842.               end;
  843.             end;
  844.           end;
  845.         except
  846.         end;
  847.         node := GetNext(node);
  848.       until node = nil;
  849.     end;
  850.   finally
  851.     ListMsg.EndUpdate;
  852.     sms.Free;
  853.   end;
  854. end;
  855.  
  856. procedure TfrmMsgView.ListMsgAfterPaint(Sender: TBaseVirtualTree;
  857.   TargetCanvas: TCanvas);
  858. begin
  859.   NoItemsPanel.Visible := ListMsg.ChildCount[nil] = 0;
  860. end;
  861.  
  862. procedure TfrmMsgView.pmListMsgPopup(Sender: TObject);
  863. var
  864.   node: PVirtualNode;
  865.   item: PListData;
  866.   HasRead,HasUnread: boolean;
  867. begin
  868.   HasRead := False;
  869.   HasUnread := False;
  870.   node := ListMsg.GetFirst;
  871.   Repeat
  872.     if ListMsg.Selected[node] then begin
  873.       item := ListMsg.GetNodeData(node);
  874.       if item.newmsg then HasUnread := True
  875.         else HasRead := True;
  876.       if HasRead and HasUnread then break;
  877.     end;
  878.     node := ListMsg.GetNext(node);
  879.   Until node = nil;
  880.   SendMessage1.Visible := Form1.Explorer.Selected = Form1.FNodeMsgDrafts;
  881.   Newmessage1.Visible := not SendMessage1.Visible;
  882.   Detail1.Enabled := ListMsg.SelectedCount = 1;
  883.   SendMessage1.Enabled := Detail1.Enabled;
  884.   MarkasRead1.Enabled := HasUnread;
  885.   MarkasUnread1.Enabled := HasRead;
  886.   SendTO.Enabled := Form1.Explorer.Selected = Form1.FNodeMsgArchive;
  887.   SendTO.Visible := not SendMessage1.Visible;
  888. end;
  889.  
  890. procedure TfrmMsgView.btnDELClick(Sender: TObject);
  891. var
  892.   i, index: Integer;
  893.   location: Integer;
  894.   s,memType: String;
  895.   node,prev: PVirtualNode;
  896.   item: PListData;
  897.   b: boolean;
  898.   sl: TStrings;
  899. begin
  900.   if ListMsg.SelectedCount = 0 then exit;
  901.   s := 'Deleting ' + IntToStr(ListMsg.SelectedCount) + ' item(s)';
  902.   b := Form1.skipDeleteWarn;
  903.   Form1.skipDeleteWarn := False;
  904.   if not b and (MessageDlg(s+'. Do you wish to continue?',mtConfirmation,[mbYes,mbNo],0) <> ID_YES) then
  905.     exit;
  906.   Form1.Status(s+'...');
  907.   Update;
  908.   frmConnect := GetProgressDialog;
  909.   try
  910.     if Form1.CanShowProgress then
  911.       frmConnect.ShowProgress(Form1.FProgressLongOnly);
  912.     frmConnect.Initialize(ListMsg.SelectedCount,s);
  913.     ListMsg.Enabled := False; // prevent keyboard move up/down in list while deleteing
  914.     ListMsg.BeginUpdate;
  915.     try
  916.       node := ListMsg.GetLast;
  917.       while node <> nil do begin
  918.         prev := nil;
  919.         try
  920.           if ListMsg.Selected[node] then begin
  921.             item := ListMsg.GetNodeData(node);
  922.             index := item.StateIndex and $FFFF;
  923.             location := (item.StateIndex and $80000) shr 16;
  924.  
  925.             if location = 0 then // ME
  926.               memType := 'ME'
  927.             else
  928.             if location = 4 then // SM
  929.               memType := 'SM'
  930.             else // 8, PC
  931.               memType := '';
  932.  
  933.             { If deleteing from Outbox, notify and enable Chat window }
  934.             if Form1.Explorer.Selected = Form1.FNodeMsgOutbox then
  935.               Form1.ChatNotifyDel(item.pdu);
  936.  
  937.             { Remove message from database }
  938.             sl := TStrings(Form1.Explorer.Selected.Data);
  939.             for i := 0 to sl.Count - 1 do begin
  940.               if WideCompareText(GetToken(sl[i], 5),item.pdu) = 0 then begin
  941.                 if memType <> '' then begin
  942.                   Form1.RequestConnection;
  943.                   Form1.DeleteSMS(index, memType);
  944.                 end;
  945.                 sl.Delete(i);
  946.                 prev := node;
  947.                 break;
  948.               end;
  949.             end;
  950.  
  951.             { Update progress }
  952.             frmConnect.IncProgress(1);
  953.           end;
  954.         finally
  955.           node := ListMsg.GetPrevious(node);
  956.           { Remove already deleted message's node }
  957.           if Assigned(prev) then begin
  958.             { If deleteing current node, clear personalization and msg preview too }
  959.             if prev = ListMsg.FocusedNode then begin
  960.               MemoMsgBody.Clear;
  961.               IsCustomImage := False;
  962.             end;
  963.             ListMsg.DeleteNode(prev);
  964.           end;
  965.         end;
  966.       end;
  967.     finally
  968.       ListMsg.EndUpdate;
  969.       ListMsg.Enabled := true;
  970.       Form1.UpdateNewMessagesCounter(Form1.Explorer.Selected);
  971.     end;
  972.   finally
  973.     FreeProgressDialog;
  974.   end;
  975.   Form1.Status('');
  976. end;
  977.  
  978. procedure TfrmMsgView.SpeedButton1Click(Sender: TObject);
  979. begin
  980.   Form1.ActionViewMsgPreview.Execute;
  981. end;
  982.  
  983. procedure TfrmMsgView.ListMsgKeyDown(Sender: TObject; var Key: Word;
  984.   Shift: TShiftState);
  985. begin
  986.   if (Key = VK_RETURN) and (ListMsg.SelectedCount = 1) then
  987.     Detail1Click(nil);
  988. end;
  989.  
  990. procedure TfrmMsgView.Splitter2Moved(Sender: TObject);
  991. begin
  992.  if PreviewPanel.Height < Splitter2.MinSize then PreviewPanel.Height := Splitter2.MinSize;
  993. end;
  994.  
  995. procedure TfrmMsgView.ListMsgChange(Sender: TBaseVirtualTree;
  996.   Node: PVirtualNode);
  997. begin
  998.   Timer1.Enabled := False;
  999.   Timer1.Interval := 4000;
  1000.   Timer1.Enabled := (ListMsg.SelectedCount = 1) and PreviewPanel.Visible and
  1001.     (Form1.Explorer.Selected <> Form1.FNodeMsgDrafts);
  1002. end;
  1003.  
  1004. procedure TfrmMsgView.Timer1Timer(Sender: TObject);
  1005. var
  1006.   item: PListData;
  1007. begin
  1008.   Timer1.Enabled := False;
  1009.   if ListMsg.FocusedNode = nil then exit;
  1010.  
  1011.   item := ListMsg.GetNodeData(ListMsg.FocusedNode);
  1012.   if item.newmsg then begin
  1013.     Form1.UpdateNewMessagesCounter(Form1.Explorer.Selected,item.pdu);
  1014.     item.newmsg := False;
  1015.     ListMsg.Update;
  1016.   end;
  1017. end;
  1018.  
  1019. procedure TfrmMsgView.MarkasReadUnreadClick(Sender: TObject);
  1020. var
  1021.   node: PVirtualNode;
  1022.   item: PListData;
  1023.   AsRead: boolean;
  1024. begin
  1025.   AsRead := TMenuItem(Sender).Tag <> 0;
  1026.   node := ListMsg.GetFirst;
  1027.   Repeat
  1028.     if ListMsg.Selected[node] then begin
  1029.       item := ListMsg.GetNodeData(node);
  1030.       if item.newmsg = AsRead then begin
  1031.         Form1.UpdateNewMessagesCounter(Form1.Explorer.Selected,item.pdu,AsRead);
  1032.         item.newmsg := not AsRead;
  1033.       end;
  1034.     end;
  1035.     node := ListMsg.GetNext(node);
  1036.   Until node = nil;
  1037.   ListMsg.Update;
  1038. end;
  1039.  
  1040. procedure TfrmMsgView.ListMsgDblClick(Sender: TObject);
  1041. begin
  1042.   if Form1.Explorer.Selected = Form1.FNodeMsgDrafts then
  1043.     SendMessage1.Click
  1044.   else
  1045.     Detail1.Click;
  1046. end;
  1047.  
  1048. procedure TfrmMsgView.SendMessage1Click(Sender: TObject);
  1049. var
  1050.   node: PVirtualNode;
  1051.   item: PListData;
  1052. begin
  1053.   node := ListMsg.GetFirst;
  1054.   Repeat
  1055.     if ListMsg.Selected[node] then begin
  1056.       item := ListMsg.GetNodeData(node);
  1057.       Form1.ActionSMSNewMsg.Execute;
  1058.       frmMessageContact.AddRecipient(item.from);
  1059.       frmMessageContact.Memo.Text := item.msg;
  1060.       frmMessageContact.Memo.SelStart := Length(item.msg);
  1061.     end;
  1062.     node := ListMsg.GetNext(node);
  1063.   Until node = nil;
  1064.   ListMsg.Update;
  1065. end;
  1066.  
  1067. procedure TfrmMsgView.ImportTextMessages1Click(Sender: TObject);
  1068. var
  1069.   ImpList: TStringList;
  1070.   sl: TStrings;
  1071.   t,str: WideString;
  1072.   i,j,Added,iBody,iDate,iState,iPDU,iNew: integer;
  1073.   function IsMultilineBody(s: WideString): boolean;
  1074.   var
  1075.     i,j: integer;
  1076.   begin
  1077.     j := 0;
  1078.     for i := 1 to length(s) do
  1079.       if s[i] = '"' then inc(j);
  1080.     Result := j mod 2 <> 0;
  1081.   end;
  1082. begin
  1083.   if (Form1.Explorer.Selected = nil) or not OpenDialog1.Execute then exit;
  1084.   sl := TStrings(Form1.Explorer.Selected.Data);
  1085.   Added := 0;
  1086.   ImpList := TStringList.Create;
  1087.   try
  1088.     ImpList.LoadFromFile(OpenDialog1.FileName);
  1089.     if ImpList.Count <= 1 then raise Exception.Create('Nothing to import');
  1090.  
  1091.     Form1.Status('Importing messages...');
  1092.  
  1093.     iBody := 0; iDate := 0; iState := 0; iPDU := 0; iNew := 0;
  1094.     i := 0; str := '';
  1095.     while i < ImpList.Count do begin
  1096.       if Trim(ImpList[i]) <> '' then begin
  1097.         str := str + ImpList[i];
  1098.         if not IsMultilineBody(str) then begin
  1099.           if iBody = 0 then begin // find fields mapping
  1100.             for j := 0 to GetTokenCount(str)-1 do begin
  1101.               // "Subject","Body","From: (Name)","From: (Address)","From: (Type)","To: (Name)","To: (Address)","To: (Type)",
  1102.               // "Fma Date","Fma State","Fma PDU","Fma New"
  1103.               t := GetToken(str,j);
  1104.               if WideCompareText(t,'Body') = 0 then iBody := j;
  1105.               if WideCompareText(t,'Fma Date') = 0 then iDate := j;
  1106.               if WideCompareText(t,'Fma State') = 0 then iState := j;
  1107.               if WideCompareText(t,'Fma PDU') = 0 then iPDU := j;
  1108.               if WideCompareText(t,'Fma IsNew') = 0 then iNew := j;
  1109.             end;
  1110.           end
  1111.           else begin
  1112.             t := '3,' + IntToStr(sl.Count+1);
  1113.             if StrToInt(GetToken(str,iState)) and $2000 <> 0 then // outgoing message
  1114.               t := t + ',3,,,'
  1115.             else
  1116.               t := t + ',1,,,';
  1117.             t := t + '"' + GetToken(str,iPDU) + '","' + GetToken(str,iDate) + '",' + GetToken(str,iNew);
  1118.             { TODO: Avoid dublicated messages } 
  1119.             sl.Add(t);
  1120.             inc(Added);
  1121.           end;
  1122.           str := '';
  1123.         end;  
  1124.       end;
  1125.       inc(i);
  1126.     end;
  1127.   finally
  1128.     ImpList.Free;
  1129.     if Added <> 0 then begin
  1130.       Form1.UpdateNewMessagesCounter(Form1.Explorer.Selected);
  1131.       RenderListView(sl);
  1132.       Form1.Debug('Imported '+IntToStr(Added)+' item(s)...');
  1133.     end;
  1134.   end;
  1135.   Form1.Status('Import complete.');
  1136. end;
  1137.  
  1138. procedure TfrmMsgView.Set_CustomImage(const Value: Boolean);
  1139. begin
  1140.   if not Value and (FCustomImage <> Value) then
  1141.     SelImage.Bitmap.Assign(Form1.CommonBitmaps.Bitmap[0]);
  1142.   FCustomImage := Value;
  1143. end;
  1144.  
  1145. end.
  1146.  
  1147.